﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;
using RW.Data.Objects;

namespace RW.Data
{
    public class LogicStatement
    {
        protected string Statement { get; set; }

        /// <summary>
        /// SAMPLE
        /// statement: (singing and ~alive
        /// will be transformed into: singing && !alive
        /// </summary>
        public LogicStatement(string statement)
        {
            if (!String.IsNullOrEmpty(statement))
            {
                this.Statement = statement.Replace("~", "!");

                string[] split = this.Statement.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                for (int i = 0; i < split.Count(); i++)
                {
                    if (split[i] == "or")
                        split[i] = "||";
                    if (split[i] == "and")
                        split[i] = "&&";
                    if (split[i] == "=>")
                    {
                        split[i - 1] = "!" + split[i - 1];
                        split[i] = "||";
                    }

                    //TODO
                    //else if(split[i] == "<==>")
                    //{

                    //}
                }

                this.Statement = String.Join(" ", split);
            }
        }

        /// <summary>
        /// SAMPLE
        /// statement: singing && !alive
        /// variables will be replaced with their values e.g. true && !false
        /// and executed as C# code
        /// </summary>
        public bool Execute(State state)
        {
            if (String.IsNullOrEmpty(this.Statement))
                return true;

            string statement = this.Statement;

            foreach (KeyValuePair<string, bool> variable in state)
                statement = statement.Replace(variable.Key, variable.Value.ToString().ToLower());

            return (bool)this.Eval(statement);
        }

        private object Eval(string code)
        {
            CSharpCodeProvider codeProvider = new CSharpCodeProvider();
            CompilerParameters compilerParameters = new CompilerParameters()
                                                        {
                                                            GenerateInMemory = true,
                                                            GenerateExecutable = false
                                                        };

            compilerParameters.ReferencedAssemblies.Add("system.dll");

            string codeEvalerCode = String.Format(
                @"using System;
                    namespace CSCodeEvaler
                    {{
                        public class CSCodeEvaler
                        {{
                            public object EvalCode()
                            {{
                                return {0};
                            }}
                        }}
                    }}",
                code);

            CompilerResults compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, codeEvalerCode);

            if (compilerResults.Errors.Count > 0)
                throw new Exception("Something is not working :/");

            Assembly assembly = compilerResults.CompiledAssembly;
            object cSCodeEvalerInstance = assembly.CreateInstance("CSCodeEvaler.CSCodeEvaler");
            Type type = cSCodeEvalerInstance.GetType();
            MethodInfo evalCodeMethodInfo = type.GetMethod("EvalCode");

            return evalCodeMethodInfo.Invoke(cSCodeEvalerInstance, null);
        }
    }
}
